/**
 *  This file is part of PennerBot
 *
 *  Copyright (C) 2010 microkernel
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

//TODO randomvisit
//TODO spenden

#include <core/bot.hpp>
#include <core/messages.hpp>
#include <core/strings.hpp>

#include <core/modules/bottlecollect.hpp>
#include <core/modules/bottlesell.hpp>

#ifdef PBOT_SPECIAL
#include <core/modules/autoskill.hpp>
#endif /* PBOT_SPECIAL */

namespace PBot
{
namespace Core
{

Bot::Bot(Config* _cfg) :
    cfg(_cfg), str(Strings::GetInstance()), con(_cfg, P_StatusListener::GetInstance()), listener(P_StatusListener::GetInstance()),
            status(START), active(true), loginAttempts(0)
{
    modules.push_back(new BottleCollect(con, cfg));
    modules.push_back(new BottleSell(con, cfg));

#ifdef PBOT_SPECIAL
    modules.push_back(new AutoSkill(con, cfg));
#endif /* PBOT_SPECIAL */

}

Bot::~Bot()
{
    for(std::vector<Module*>::iterator i(modules.begin()), end(modules.end()); i != end; ++i)
    {
        delete *i;
    }

    delete cfg;
}

void Bot::Do()
{
    if(IsAwake() && active)
    {
        switch(status)
        {
            case START:
                if(con.Init())
                {
                    status = PAUSED;
                }
                listener.Dispatch(cfg->id, MSG_BOT_STARTED);
                status = NOTLOGGEDIN;
                Sleep(1,2);
                break;

            case NOTLOGGEDIN:
                if(Login())
                {
                    status = LOGGEDIN;
                }
                Sleep(2, 5);

                if(loginAttempts >= MAX_LOGIN_ATTEMPTS)
                {
                    listener.Dispatch(cfg->id, MSG_BOT_LOGINATTEMPTS);
                    Sleep(10*60);
                    loginAttempts = 0;
                }

                break;

            case LOGGEDIN:
                for(std::vector<Module*>::iterator i(modules.begin()), end(modules.end()); i != end;)
                {
                    if((*i)->GetAttempts() >= MAX_MODULE_ATTEMPTS)
                    {
                        (*i)->TooManyAttempts();
                        modules.erase(i++);
                        continue;
                    }

                    if((*i)->IsAwake())
                    {
                        if(!(*i)->Do())
                        {
                            (*i)->IncrementAttempts();
                            status = NOTLOGGEDIN;
                            break;
                        }

                        (*i)->ResetAttempts();
                    }

                    ++i;
                }

                break;

            case PAUSED:
                break;

        }
    }
}

bool Bot::IsActive()
{
    return active;
}

void Bot::Stop()
{
    active = false;
}

bool Bot::Login()
{
    std::string url = cfg->website;

    if(loginAttempts < MAX_LOGIN_ATTEMPTS)
    {
        listener.Dispatch(cfg->id, MSG_BOT_LOGINTRY, loginAttempts);

        if(!con.Get(url))
        {
            ++loginAttempts;
            return false;
        }

        if(con.GetBuffer().find(str.GetString(cfg->game, Strings::LOGIN_PASSED)) != std::string::npos)
        {
            listener.Dispatch(cfg->id, MSG_BOT_ALREADYLOGGEDIN);
            return true;
        }

        std::string inputUsername;
        if(!str.GetRegex(cfg->game, Strings::LOGIN_USERNAME)->Search(con.GetBuffer(), inputUsername))
        {
            listener.Dispatch(cfg->id, MSG_BOT_LOGINCHANGE);
            ++loginAttempts;
            return false;
        }

        std::string inputPassword;
        if(!str.GetRegex(cfg->game, Strings::LOGIN_PASSWORD)->Search(con.GetBuffer(), inputPassword))
        {
            listener.Dispatch(cfg->id, MSG_BOT_LOGINCHANGE);
            ++loginAttempts;
            return false;
        }

        std::string action;
        std::string escapedAction;

        std::string escapedUsername = con.Escape(cfg->username);
        std::string escapedPassword = con.Escape(cfg->password);

        std::string postdata;

        if(escapedUsername == "" || escapedPassword == "")
        {
            ++loginAttempts;
            return false;
        }

        switch(cfg->game)
        {
            case Config::PENNERGAME_H:
            case Config::PENNERGAME_B:
            case Config::PENNERGAME_M:
            case Config::MENELGAME_W:
            case Config::MENELGAME_K:
            case Config::CLODOGAME_P:
            case Config::CLODOGAME_M:


                if(!str.GetRegex(cfg->game, Strings::LOGIN_CITY)->Search(con.GetBuffer(), action))
                {
                    listener.Dispatch(cfg->id, MSG_BOT_LOGINCHANGE);
                    ++loginAttempts;
                    return false;
                }

                escapedAction = con.Escape(action);

                if(escapedAction == "")
                {
                    ++loginAttempts;
                    return false;
                }

                postdata = inputUsername+"="+escapedUsername+"&"+inputPassword+"="+escapedPassword+"&city_dropdown="+escapedAction;

                break;

            default:
                action = cfg->website+"/login/check/";
                postdata = inputUsername+"="+escapedUsername+"&"+inputPassword+"="+escapedPassword;
                break;
        }

        con.Post(action, postdata);

        if(con.GetBuffer().find(str.GetString(cfg->game, Strings::LOGIN_PASSED)) != std::string::npos)
        {
            listener.Dispatch(cfg->id, MSG_BOT_LOGGEDIN);
            return true;
        }
        else if(con.GetBuffer().find(str.GetString(cfg->game, Strings::LOGIN_WRONGPASS)) != std::string::npos)
        {
            listener.Dispatch(cfg->id, MSG_BOT_WRONGPASS);
            status = PAUSED;
            return false;
        }
        else
        {
            ++loginAttempts;
            return false;
        }

    }

    return false;
}

}
}
